from BD import *

bdConn = BD();

numTypes = [float, int]


class DatosValidacion():
    def __init__(self):
        self.errores = []
        self.tablas = []
        self.pkCounter = 0
        
    def esValido(self):
        return len(self.errores) == 0
    
    def agregarTabla(self, tabla):
        self.tablas.append(tabla)
        
    def limpiarTablas(self):
        self.tablas = []
    
    def reset(self):
        self.errores = []
        self.tablas = []
        self.pkCounter = 0        

validData = DatosValidacion()

class DatosEvaluacion():
    def __init__(self, root = False, proyConFunc = False):
        self.proyecciones = []
        self.tablas = []
        self.restricciones = []
        self.order = []
        self.group = []
        self.alias = ''
        self.root = root
        self.proyConFunc = proyConFunc
        self.pkCounter = 0

    def compilar(self):
        if (self.root == False):
            if (len(self.proyecciones) + len(self.restricciones) + len(self.order) == 0):
                query = self.tablas[0]
                if (len(self.tablas) > 2):
                    for index in range(1,len(self.tablas)):
                        query += ',' + self.tablas[index]
                return query
        query = 'SELECT '
        if (len(self.proyecciones) > 0):
            query += self.proyecciones[0]
            if (self.proyConFunc) & (not '(' in self.proyecciones[0]):
                self.group.append(self.proyecciones[0])
            if (len(self.proyecciones) > 1):
                for index in range(1,len(self.proyecciones)):
                    query += ',' + self.proyecciones[index]
                    if (self.proyConFunc) & (not '(' in self.proyecciones[index]):
                        self.group.append(self.proyecciones[index])
        else:
            query += '*'
        query += ' \nFROM '
        
        query += self.tablas[0]
        if (len(self.tablas) > 1):
            for index in range(1,len(self.tablas)):
                query += ',' + self.tablas[index]
        if (len(self.restricciones) > 0):
            query += ' \nWHERE ' + self.restricciones[0]
            if (len(self.restricciones) > 1):
                for index in range(1,len(self.restricciones)):
                    query += ' AND ' + self.restricciones[index]
        
        if (len(self.group) > 0):
            query += ' \nGROUP BY ' + self.group[0]
            if (len(self.group) > 1):
                for index in range(1,len(self.group)):
                    query += ', ' + self.group[index]
                    
        if (len(self.order) > 0):
            query += ' \nORDER BY' + self.order[0]
            if (len(self.order) > 1):
                for index in range(1,len(self.order)):
                    query += ',' + self.order[index]
                    
        if (self.alias != ''):
            query = '(' + query + ') ' + self.alias
            
        #if (self.root):
        #    query += ';'
        return query
        
evalData = None

class Consulta():
    def __init__(self,tabla,proyecciones = None,restricciones = None):
        self.tabla = tabla
        self.proyecciones = proyecciones
        self.restricciones = restricciones
    
    def imprimir(self):
        if ((self.proyecciones != None) & (self.restricciones != None)):
            return self.tabla.imprimir() + '[' + self.proyecciones.imprimir() + '](' + self.restricciones.imprimir() + ')'
        elif (self.proyecciones != None):
            return self.tabla.imprimir() + '[' + self.proyecciones.imprimir() + ']'
        elif (self.restricciones != None):
            return self.tabla.imprimir() + '(' + self.restricciones.imprimir() + ')'
        else:
            return self.tabla.imprimir()
    
    def validar(self, esIn = False):
        global validData
        if self.tabla != None:
            self.tabla.validar()
        if self.proyecciones != None:
            if esIn:
                if self.proyecciones.__class__ == Proyecciones:
                    if self.proyecciones.proy2 != None:
                        validData.errores.append('Cantidad invalida de proyecciones')
            self.proyecciones.validar()
        elif esIn:
            validData.errores.append('Cantidad invalida de proyecciones')
                        
        if self.restricciones != None:
            self.restricciones.validar()
    
    def compilar(self):
        global evalData
        parentEval = None
        if (evalData != None):
            parentEval = evalData
            evalData = DatosEvaluacion()
            if (parentEval.proyConFunc):
                evalData.proyConFunc = True
        else:
            evalData = DatosEvaluacion(True)
        
        self.tabla.compilar()
        
        if (self.proyecciones != None):
            self.proyecciones.compilar()
        
        if (self.restricciones != None):    
            self.restricciones.compilar()
            
        
        if (parentEval != None):
            parentEval.tablas.append('(' + evalData.compilar() + ')')
            evalData = parentEval
        compilar = evalData.compilar()
        if (parentEval == None):
            evalData = None
        return compilar

class Tabla():
    def __init__(self,tabla):
        self.tabla = tabla
        
    def imprimir(self):
        return self.tabla
    
    def validar(self):
        global dbConn
        global validData
        if self.tabla.__class__ == ID:
            if not bdConn.tableExists(self.tabla.val):
                validData.errores.append("Nombre de tabla invalido: " + self.tabla.val)
            else:
                validData.agregarTabla(self.tabla.val)
        else:
            self.tabla.validar()
    
    def compilar(self):
        global evalData
        evalData.tablas.append(self.tabla.compilar())

class Restriccion():
    pass

class Restricciones(Restriccion):
    def __init__(self,r1,r2=None):
        self.r1 = r1
        self.r2 = r2
    
    def imprimir(self):
        if (self.r2 != None):
            return self.r1.imprimir() + ',' + self.r2.imprimir()
        else:
            return self.r1.imprimir()

    def validar(self):
        self.r1.validar()
        if (self.r2 != None):
            self.r2.validar()
    
    def compilar(self):
        global evalData
        evalData.restricciones.append(self.r1.compilar())
        if (self.r2 != None):
            evalData.restricciones.append(self.r2.compilar())

class RestriccionExp(Restriccion):
    def __init__(self,exp):
        self.exp = exp
    
    def imprimir(self):
        return self.exp.imprimir()
    
    def validar(self):
        global validData
        if (not self.exp.__class__ == Constante):
            if self.exp.validar() != bool:
                validData.errores.append("Expresion invalida " + self.imprimir())
        else:
            self.exp.validar()
        
    def compilar(self):
        c = ''
        if self.exp.__class__ == Constante:
            c = self.exp.compilar(True)
        else:
            c = self.exp.compilar()
        if c != '':
            return c

class Restriccionpk(Restriccion):
    def __init__(self,val):
        self.val = val
    
    def imprimir(self):
        return self.val.imprimir()
    
    def validar(self):
        global validData
        if self.val.validar() != bool:
            validData.errores.append("Expresion invalida " + self.imprimir())
        return bool
        
    def compilar(self):
        global evalData
        c = self.exp.compilar()
        if c != '':
            evalData.restricciones.append(c)

class RestriccionIn(Restriccion):
    def __init__(self,col,consulta,esIn = True):
        self.col = col
        self.consulta = consulta
        self.esIn = esIn
    
    def imprimir(self):
            return self.col + '->' + self.consulta.imprimir()
    
    def validar(self):
        self.consulta.validar(True)
        
    def compilar(self):
        global evalData
        edBase = evalData
        evalData = DatosEvaluacion()
        self.consulta.compilar()
        if self.esIn:
            exp = self.col + ' IN ' + evalData.compilar()
        else:
            exp = self.col + ' NOT IN ' + evalData.compilar()
        evalData = edBase
        return exp
    
class RestriccionEqual(Restriccion):
    def __init__(self,col,consulta,esAll = True):
        self.col = col
        self.consulta = consulta
        self.esAll = esAll
    
    def imprimir(self):
            return self.col + '->' + self.consulta.imprimir()
    
    def validar(self):
        self.consulta.validar(True)
        
    def compilar(self):
        global evalData
        edBase = evalData
        evalData = DatosEvaluacion()
        self.consulta.compilar()
        if self.esAll:
            exp = self.col + ' = ALL ' + evalData.compilar()
        else:
            exp = self.col + ' < ANY ' + evalData.compilar()
        evalData = edBase
        return exp
                     
class Proyeccion():
    pass

class Proyecciones(Proyeccion):
    def __init__(self, proy1, proy2 = None):
        self.proy1 = proy1
        self.proy2 = proy2
        
    def imprimir(self):
        if (self.proy2 != None):
            return self.proy1.imprimir() + ',' + self.proy2.imprimir()
        else:
            return self.proy1.imprimir()
    
    def validar(self, esIn = False):
        global validData
        if (self.proy2 != None):
            self.proy1.validar()
            self.proy2.validar()
            if esIn:
                validData.errores.append('Debe tener solo una proyeccion la consulta luego de <-')
        else:
            self.proy1.validar()
    
    def compilar(self):
        global evalData
        self.proy1.compilar()
        if (self.proy2 != None):
            self.proy2.compilar()

class ProyeccionColumna(Proyeccion):
    def __init__(self, col):
        self.col = col
        
    def imprimir(self):
        return self.col
    
    def validar(self):
        self.col.validar()
            
    def compilar(self):
        global evalData
        evalData.proyecciones.append(self.col.compilar())

# Se utiliza cuando solo se especifica el nombre de la columna
# Ex: select columna1 from tabla;
# Este tipo de proyeccion corresponderia a columna1 en el ejemplo
class Proyeccion_simple(Proyeccion):
    
    def __init__(self, columna):
        self.columna = columna
    
    def imprimir(self):
        return self.columna
    
    def validar(self):
        global bdConn
        global validData
        if not bdConn.existe_campo_en_tabla(self.columna, validData.tablas):
            validData.errores.append("Columna invalida: " + self.columna + " en tablas: " + str(validData.tablas))
    
    def compilar(self):
        global evalData
        evalData.proyecciones.append(self.columna)

#Proyeccion con nombre (ID as ALIAS)
class ProyeccionConNombre(Proyeccion):
    def __init__(self, nombre, columna):
        self.columna = columna
        self.nombre = nombre
    
    def imprimir(self):
        return self.nombre + ' : ' + self.columna.imprimir()
    
    def validar(self):
        self.columna.validar()
    
    def compilar(self):
        global evalData
        compilar = self.columna.compilar() + ' as ' + self.nombre
        evalData.proyecciones.append(compilar) 

class ForeignKey(Proyeccion):
    def __init__(self,fk,col):
        self.fk = fk
        self.columna = col
    
    def imprimir(self):
        return self.fk.imprimir() + '->' + self.columna.imprimir()
    
    def validar(self, tabla = None):
        global validData
        global bdConn
        if (tabla == None):
            tLocal = validData.tablas[0]
        else:
            tLocal = tabla
        tFTs = bdConn.getFkTable(tLocal, self.fk)
        if len(tFTs) == 0:
            validData.errores.append("FK invalida: " + self.fk)
            return
        tForeign = tFTs[0]
        self.columna.validar(tForeign)
        
    
    def compilar(self, tabla = None):
        global evalData
        global bdConn
        if (tabla == None):
            tLocal = evalData.tablas[-1]
            if 'JOIN' in tLocal:
                tLocal = tLocal[0:tLocal.index(' ')-1]
        else:
            tLocal = tabla
        tFTs = bdConn.getFkTable(tLocal, self.fk)
        tForeign = tFTs[0]
        pkForeign = bdConn.getTablePKs(tForeign)[0]
        foreign = tLocal + '.' + self.fk

        evalData.tablas[0] += '\n    JOIN ' + tForeign + ' ON ' + foreign + ' = ' + tForeign + '.' + pkForeign  
        return self.columna.compilar(tForeign)

class OrderBy(Proyeccion):
    def __init__(self,col,asc = True):
        self.col = col
        self.asc = asc
    
    def imprimir(self):
        if (not self.asc):
            return "<:" + self.col
        else:
            return ">:" + self.col
    
    def validar(self):
        global validData
        if (type(self.col) != str):
            validData.errores.append("Tipo invalido en OrderBy " + self.imprimir())
        if not bdConn.existe_campo_en_tabla(self.col, validData.tablas):
            validData.errores.append("Columna invalida en " + self.imprimir())
            
    def compilar(self):
        global evalData
        order = ' '
        order += self.col
        if (self.asc):
            order += ' ASC'
        else:
            order += ' DESC'
        evalData.order.append(order)
        evalData.proyecciones.append(self.col)

class ProyCount(Proyeccion):
    def __init__(self,col,distinct = False):
        self.col = col
        self.distinct = distinct
    
    def imprimir(self):
        if self.distinct:
            return "##(" + self.col + ")"
        else:
            return "#(" + self.col + ")"
    
    def validar(self):
        global validData
        if (type(self.col) != str):
            validData.errores.append("Nombre invalido en " + self.imprimir())
        if not bdConn.existe_campo_en_tabla(self.col, validData.tablas):
            validData.errores.append("Columna invalida en " + self.imprimir())
    
    def compilar(self):
        global evalData
        strCount = 'COUNT('
        if (self.distinct):
            strCount += 'DISTINCT(' + self.col + ')'
        else:
            strCount += self.col
        strCount += ')'
        evalData.proyConFunc = True
        evalData.proyecciones.append(strCount)
        
class ProySum(Proyeccion):
    def __init__(self,col):
        self.col = col
    
    def imprimir(self):
        return "@+(" + self.col + ")"
    
    def validar(self):
        global validData
        global bdConn
        if (type(self.col) != str):
            validData.errores.append("Nombre invalido en " + self.imprimir())
        if not bdConn.existe_campo_en_tabla(self.col, validData.tablas):
            validData.errores.append("Columna invalida en " + self.imprimir())
            
                
    def compilar(self):
        global evalData
        strMax = 'SUM(' + self.col + ')'
        evalData.proyConFunc = True
        evalData.proyecciones.append(strMax)

class ProyAvg(Proyeccion):
    def __init__(self,col):
        self.col = col
    
    def imprimir(self):
        return "@-(" + self.col + ")"
    
    def validar(self):
        global validData
        if (type(self.col) != str):
            validData.errores.append("Nombre invalido en " + self.imprimir())
        if not bdConn.existe_campo_en_tabla(self.col, validData.tablas):
            validData.errores.append("Columna invalida en " + self.imprimir())
        
    def compilar(self):
        global evalData
        strMax = 'AVG(' + self.col + ')'
        evalData.proyConFunc = True
        evalData.proyecciones.append(strMax)
        
class ProyMax(Proyeccion):
    def __init__(self,col):
        self.col = col
    
    def imprimir(self):
        return "@>(" + self.col + ")"
    
    def validar(self):
        global validData
        if (type(self.col) != str):
            validData.errores.append("Nombre invalido en " + self.imprimir())
        if not bdConn.existe_campo_en_tabla(self.col, validData.tablas):
            validData.errores.append("Columna invalida en " + self.imprimir())
        
    def compilar(self):
        global evalData
        strMax = 'MAX(' + self.col + ')'
        evalData.proyConFunc = True
        evalData.proyecciones.append(strMax)
        
class ProyMin(Proyeccion):
    def __init__(self,col):
        self.col = col
    
    def imprimir(self):
        return "@<(" + self.col + ")"
    
    def validar(self):
        global validData
        if (type(self.col) != str):
            validData.errores.append("Nombre invalido en " + self.imprimir())
        if not bdConn.existe_campo_en_tabla(self.col, validData.tablas):
            validData.errores.append("Columna invalida en " + self.imprimir())
        
    def compilar(self):
        global evalData
        strMin = 'MIN(' + self.col + ')'
        evalData.proyConFunc = True
        evalData.proyecciones.append(strMin)

# Se utiliza para el Count
class Count(Proyeccion):
    def imprimir(self):
        return 'count(*)'
    
    def validar(self):
        pass
    
    def compilar(self):
        evalData.proyecciones.append('count(*)')
    
# Crear uniones de consultas
class Union():
    def __init__(self, consulta1, consulta2):
        self.consulta1 = consulta1
        self.consulta2 = consulta2
        
    def imprimir(self):
        return "(" + self.consulta1.imprimir()+ ") UNION (" + self.consulta2.imprimir() + ")"
    
    def validar(self):
        global validData
        baseVD = validData
        validData = DatosValidacion()
        self.consulta1.validar()
        baseVD.errores += validData.errores
        baseVD.tablas += validData.tablas
        validData = DatosValidacion()
        self.consulta2.validar()
        baseVD.errores += validData.errores
        baseVD.tablas += validData.tablas
        validData = baseVD
    
    def compilar(self):
        c1 = self.consulta1.compilar()
        c1 = c1[0:len(c1)]
        c2 = self.consulta2.compilar()
        c2 = c2[0:len(c2)]
        return "(" + c1 + ") UNION (" + c2 + ")"
    
# Crear Intersecciones de consultas
class Intersect():
    def __init__(self, consulta1, consulta2):
        self.consulta1 = consulta1
        self.consulta2 = consulta2
        
    def imprimir(self):
        return self.consulta1.imprimir()+ " INTERSECT " + self.consulta2.imprimir()
    
    def validar(self):
        global validData
        baseVD = validData
        validData = DatosValidacion()
        self.consulta1.validar()
        baseVD.errores += validData.errores
        baseVD.tablas += validData.tablas
        validData = DatosValidacion()
        self.consulta2.validar()
        baseVD.errores += validData.errores
        baseVD.tablas += validData.tablas
        validData = baseVD
    
    def compilar(self):
        c1 = self.consulta1.compilar()
        c1 = c1[0:len(c1)]
        c2 = self.consulta2.compilar()
        c2 = c2[0:len(c2)]
        return "(" + c1 + ") INTERSECT (" + c2 + ")"
    
# Crear resta de consultas
class Minus():
    def __init__(self, consulta1, consulta2):
        self.consulta1 = consulta1
        self.consulta2 = consulta2
        
    def imprimir(self):
        return self.consulta1.imprimir()+ " MINUS " + self.consulta2.imprimir()
    
    def validar(self):
        global validData
        baseVD = validData
        validData = DatosValidacion()
        self.consulta1.validar()
        baseVD.errores += validData.errores
        baseVD.tablas += validData.tablas
        validData = DatosValidacion()
        self.consulta2.validar()
        baseVD.errores += validData.errores
        baseVD.tablas += validData.tablas
        validData = baseVD
    
    def compilar(self):
        c1 = self.consulta1.compilar()
        c1 = c1[0:len(c1)]
        c2 = self.consulta2.compilar()
        c2 = c2[0:len(c2)]
        return "(" +  c1 + ") MINUS (" + c2 + ")"
    
# Crear Union all de consultas
class Add():
    def __init__(self, consulta1, consulta2):
        self.consulta1 = consulta1
        self.consulta2 = consulta2
        
    def imprimir(self):
        return self.consulta1.imprimir()+ " UNION ALL " + self.consulta2.imprimir()
    
    def validar(self):
        global validData
        baseVD = validData
        validData = DatosValidacion()
        self.consulta1.validar()
        baseVD.errores += validData.errores
        baseVD.tablas += validData.tablas
        validData = DatosValidacion()
        self.consulta2.validar()
        baseVD.errores += validData.errores
        baseVD.tablas += validData.tablas
        validData = baseVD
    
    def compilar(self):
        c1 = self.consulta1.compilar()
        c1 = c1[0:len(c1)]
        c2 = self.consulta2.compilar()
        c2 = c2[0:len(c2)]
        return "(" +  c1 + ") UNION ALL (" + c2 + ")"
    
#Seleccion: Producto carteciano
class Cartesiano():
    def __init__(self, consulta1, consulta2):
        self.consulta1 = consulta1
        self.consulta2 = consulta2
        
    def imprimir(self):
        return self.consulta1.imprimir()+ " , " + self.consulta2.imprimir()
    
    def validar(self):
        global validData
        baseVD = validData
        validData = DatosValidacion()
        self.consulta1.validar()
        baseVD.errores += validData.errores
        baseVD.tablas += validData.tablas
        validData = DatosValidacion()
        self.consulta2.validar()
        baseVD.errores += validData.errores
        baseVD.tablas += validData.tablas
        validData = baseVD
    
    def compilar(self):
        return self.consulta1.compilar()+ " , " + self.consulta2.compilar()
    
class Join():
    def __init__(self, consulta1, consulta2, tipo = 'join'):
        self.consulta1 = consulta1
        self.consulta2 = consulta2
        self.tipo = tipo
        
    def imprimir(self):
        return self.consulta1.imprimir()+ " >< " + self.consulta2.imprimir()
    
    def validar(self):
        global validData
        baseVD = validData
        validData = DatosValidacion()
        self.consulta1.validar()
        baseVD.errores += validData.errores
        baseVD.tablas += validData.tablas
        validData = DatosValidacion()
        self.consulta2.validar()
        baseVD.errores += validData.errores
        baseVD.tablas += validData.tablas
        validData = baseVD
    
    def compilar(self, padre = '', padreTipo = ''):
        global evalData
        baseED = evalData
        evalData = DatosEvaluacion()
        c1 = self.consulta1.compilar()
        t1 = str(evalData.compilar()).replace('(','').replace(')','')
        sTablas = ''
        esPadre = True
        if padre == '':
            padre = t1
            sTablas = t1
        else:
            esPadre = False
            if padreTipo == 'join':
                sTablas += '\n JOIN ' + t1
            elif padreTipo == 'full':
                sTablas += '\n FULL OUTER JOIN ' + t1
            elif padreTipo == 'left':
                sTablas += '\n LEFT OUTER JOIN ' + t1
            elif padreTipo == 'right':
                sTablas += '\n RIGHT OUTER JOIN ' + t1
            else:
                sTablas += ' OJO ' + t1
                
            campos = bdConn.campos_join(padre, t1)
            if len(campos) > 0:
                sTablas += ' ON'
                for i in range(len(campos)):
                    campo = campos[i]
                    if i > 0:
                        sTablas += ','
                    sTablas += ' ' + padre + '.' + campo + ' = ' + t1 + '.' + campo + ','
            else:
                print 'No existen campos en comun entre las tablas del Join: ' + padre + ' y '+ t1
            
        if self.consulta2.__class__ == Consulta:
            evalData = DatosEvaluacion()
            c2 = self.consulta2.compilar()
            t2 = str(evalData.compilar()).replace('(','').replace(')','')
            if self.tipo == 'join':
                sTablas += '\n JOIN ' + t2
            elif self.tipo == 'full':
                sTablas += '\n FULL OUTER JOIN ' + t2
            elif self.tipo == 'left':
                sTablas += '\n LEFT OUTER JOIN ' + t2
            elif self.tipo == 'right':
                sTablas += '\n RIGHT OUTER JOIN ' + t2
            campos = bdConn.campos_join(t1, t2)
            if len(campos) > 0:
                sTablas += ' ON'
                for i in range(len(campos)):
                    campo = campos[i]
                    if i > 0:
                        sTablas += ','
                    sTablas += ' ' + t1 + '.' + campo + ' = ' + t2 + '.' + campo
            else:
                print 'No existen campos en comun entre las tablas del Join: ' + t1 + ' y '+ t2
        else:
            evalData = DatosEvaluacion()
            sTablas += self.consulta2.compilar(t1, self.tipo)
        if esPadre:
            baseED.tablas.append(sTablas)
            evalData = baseED
        else:
            return sTablas
             
class ID():
    def __init__(self, val):
        self.val = val            

    def imprimir(self):
        return str(self.val)
    
    def validar(self):
        return type(self.val)
    
    def compilar(self):
        return self.val
    
class Expresion():
    pass

class ExpresionBinarea(Expresion):
    def __init__(self,val1,val2):
        self.val1 = val1
        self.val2 = val2
        
class Suma(ExpresionBinarea):
    def imprimir(self):
        return '(' + self.val1.imprimir() + ' + ' + self.val2.imprimir() + ')'
    
    def validar(self):
        global validData
        global numTypes
        t1 = self.val1.validar()
        t2 = self.val2.validar()
        if (not t1 in numTypes) | (not t2 in numTypes):
            validData.errores.append("Expresion invalida " + self.imprimir())
        return float  
    
    def compilar(self):
        return '(' + self.val1.compilar() + ' + ' + self.val2.compilar() + ')'
    
class Resta(ExpresionBinarea):
    def imprimir(self):
        return '(' + self.val1.imprimir() + ' - ' + self.val2.imprimir() + ')'
    
    def validar(self):
        global validData
        global numTypes
        t1 = self.val1.validar()
        t2 = self.val2.validar()
        if (not t1 in numTypes) | (not t2 in numTypes):
            validData.errores.append("Expresion invalida " + self.imprimir())
            return None
        else:
            return float 
    
    
    def compilar(self):
        return '(' + self.val1.compilar() + ' - ' + self.val2.compilar() + ')'
    
class Por(ExpresionBinarea):
    def imprimir(self):
        return '(' + self.val1.imprimir() + ' * ' + self.val2.imprimir() + ')'
    
    def validar(self):
        global validData
        global numTypes
        t1 = self.val1.validar()
        t2 = self.val2.validar()
        if (not t1 in numTypes) | (not t2 in numTypes):
            validData.errores.append("Expresion invalida " + self.imprimir())
            return None
        else:
            return float 
                        
    def compilar(self):
        return '(' + self.val1.compilar() + ' * ' + self.val2.compilar() + ')'
    
class Dividir(ExpresionBinarea):
    def imprimir(self):
        return '(' + self.val1.imprimir() + ' / ' + self.val2.imprimir() + ')'
    
    def validar(self):
        global validData
        global numTypes
        t1 = self.val1.validar()
        t2 = self.val2.validar()
        if (not t1 in numTypes) | (not t2 in numTypes):
            validData.errores.append("Expresion invalida " + self.imprimir())
            return None
        else:
            return float 
            
    def compilar(self):
        return '(' + self.val1.compilar() + ' / ' + self.val2.compilar() + ')'
    
class Mayor(ExpresionBinarea):
    def imprimir(self):
        return '(' + self.val1.imprimir() + ' > ' + self.val2.imprimir() + ')'
    
    def validar(self):
        global validData
        global numTypes
        t1 = self.val1.validar()
        t2 = self.val2.validar()
        if (not t1 in numTypes) | (not t2 in numTypes):
            validData.errores.append("Expresion invalida " + self.imprimir())
            return None
        else:
            return bool
            
    def compilar(self):
        return '(' + self.val1.compilar() + ' > ' + self.val2.compilar() + ')'
    
class MayorIgual(ExpresionBinarea):
    def imprimir(self):
        return '(' + self.val1.imprimir() + ' >= ' + self.val2.imprimir() + ')'
    
    def validar(self):
        global validData
        global numTypes
        t1 = self.val1.validar()
        t2 = self.val2.validar()
        if (not t1 in numTypes) | (not t2 in numTypes):
            validData.errores.append("Expresion invalida " + self.imprimir())
            return None
        else:
            return bool
            
    def compilar(self):
        return '(' + self.val1.compilar() + ' >= ' + self.val2.compilar() + ')'
    
class Menor(ExpresionBinarea):
    def imprimir(self):
        return '(' + self.val1.imprimir() + ' < ' + self.val2.imprimir() + ')'
    
    def validar(self):
        global validData
        global numTypes
        t1 = self.val1.validar()
        t2 = self.val2.validar()
        print t2
        if (not t1 in numTypes) | (not t2 in numTypes):
            validData.errores.append("Expresion invalida " + self.imprimir())
            return None
        else:
            return bool
            
    def compilar(self):
        return '(' + self.val1.compilar() + ' < ' + self.val2.compilar() + ')'

class MenorIgual(ExpresionBinarea):
    def imprimir(self):
        return '(' + self.val1.imprimir() + ' <= ' + self.val2.imprimir() + ')'
    
    def validar(self):
        global validData
        global numTypes
        t1 = self.val1.validar()
        t2 = self.val2.validar()
        if (not t1 in numTypes) | (not t2 in numTypes):
            validData.errores.append("Expresion invalida " + self.imprimir())
            return None
        else:
            return bool
            
    def compilar(self):
        return '(' + self.val1.compilar() + ' <= ' + self.val2.compilar() + ')'

class Igual(ExpresionBinarea):
    def imprimir(self):
        return '(' + self.val1.imprimir() + ' == ' + self.val2.imprimir() + ')'
    
    def validar(self):
        global validData
        global numTypes
        t1 = self.val1.validar()
        t2 = self.val2.validar()
        if (t1 == t2):
            return bool
        else:
            validData.errores.append("Expresion invalida " + self.imprimir())
            return None
            
    def compilar(self):
        return '(' + self.val1.compilar() + ' == ' + self.val2.compilar() + ')'

class And(ExpresionBinarea):
    def imprimir(self):
        return '(' + self.val1.imprimir() + ' && ' + self.val2.imprimir() + ')'
    
    def validar(self):
        global validData
        global numTypes
        t1 = self.val1.validar()
        t2 = self.val2.validar()
        if (t1 == bool & t2 == bool):
            return bool
        else:
            validData.errores.append("Expresion invalida " + self.imprimir())
            return None
            
    def compilar(self):
        return '(' + self.val1.compilar() + ' AND ' + self.val2.compilar() + ')'

class Or(ExpresionBinarea):
    def imprimir(self):
        return '(' + self.val1.imprimir() + ' || ' + self.val2.imprimir() + ')'
    
    def validar(self):
        global validData
        global numTypes
        t1 = self.val1.validar()
        t2 = self.val2.validar()
        if (t1 == bool & t2 == bool):
            return bool
        else:
            validData.errores.append("Expresion invalida " + self.imprimir())
            return None
            
    def compilar(self):
        return '(' + self.val1.compilar() + ' OR ' + self.val2.compilar() + ')'
    
class ExpresionUnitaria(Expresion):
    def __init__(self,val):
        self.val = val
        
class Constante(ExpresionUnitaria):
    def imprimir(self):
        if not self.val.__class__ == str:
            return str(self.val)
        else:
            return "\"" +  self.val + "\""
        
    def validar(self, base = False):
        global validData
        global bdConn
        if base:
            pks = bdConn.getTablePKs(validData.tablas[0])
            if validData.pkCounter >= len(pks):
                validData.errores.append("Cantidad invalida de restricciones de pk")
            validData.pkCounter += 1   
        return (self.val).__class__
    
    def compilar(self, base = False):
        global bdConn
        global evalData
        val = self.val
        if self.val.__class__ == str:
            val = self.val.replace('"','\'')
        if not base:
            return str(val)
        else:
            pks = bdConn.getTablePKs(evalData.tablas[0])
            if evalData.pkCounter < len(pks):
                pk = pks[evalData.pkCounter]
                evalData.pkCounter += 1
                return pk + " = " + str(val)
            else:
                print "Cantidad de restricciones a claves primarias invalida"
                return ''
    
class Negacion(ExpresionUnitaria):
    def imprimir(self):
        return "!" + str(self.val)
    
    def validar(self):
        global validData
        t = self.val.validar()
        if (t != bool):
            validData.errores.append("Operacion invalida " + self.imprimir())
            return None
        return bool
    
    def compilar(self):
        return str(self.val)
    
class Columna(ExpresionUnitaria):
    def imprimir(self):
        return self.val
    
    def validar(self, tabla = None):
        global validData
        global bdConn
        t = validData.tablas[0]
        if tabla != None:
            t = tabla
        if not bdConn.existe_campo_en_tabla(self.val, [t]):
            validData.errores.append("Columna invalida:" + self.val + " en tabla "+t)
        tipo = bdConn.getColumnType(self.val, t)
        if tipo == 'NUMBER':
            return float
        elif str(tipo).startswith("VARCHAR"):
            return str
        else:
            print tipo + " no definido"
            return float
        return tipo
    
    def compilar(self, tabla = None):
        if (tabla == None):
            return self.val #TODO: Obtener de base de datos
        else:
            return tabla + '.' + self.val